import os
import geopandas as gpd
import requests
from PIL import Image
from io import BytesIO
from pyproj import Transformer
import math

# 高德地图API密钥
GAODE_API_KEY = "2a4d111f2cf8fac38dd67b856fca0cbc"


# 获取文件夹下所有SHP文件的综合边界范围
def get_combined_bbox(shp_folder):
    """
    获取文件夹下所有SHP文件的综合边界范围
    :param shp_folder: shp文件所在文件夹路径
    :return: bbox
    """
    bbox = None
    for root, _, files in os.walk(shp_folder):
        for file in files:
            if file.endswith(".shp"):
                shp_path = os.path.join(root, file)
                gdf = gpd.read_file(shp_path)
                if bbox is None:
                    bbox = gdf.total_bounds
                else:
                    bbox = [
                        min(bbox[0], gdf.total_bounds[0]),
                        min(bbox[1], gdf.total_bounds[1]),
                        max(bbox[2], gdf.total_bounds[2]),
                        max(bbox[3], gdf.total_bounds[3]),
                    ]
    return bbox


# 调用高德地图API获取底图
def get_gaode_map_image(bbox, zone):
    """
    调用高德地图API获取底图
    :param bbox: 地图范围
    :param zone: 分度带带号
    :return: 图片名称
    """
    minx, miny, maxx, maxy = bbox

    cx = (minx + maxx) / 2 + 350
    cy = (miny + maxy) / 2 - 750
    # 将CGCS2000坐标转换为WGS84经纬度
    min_lon, min_lat = cgcs2000_to_wgs84(minx, miny, zone)
    max_lon, max_lat = cgcs2000_to_wgs84(maxx, maxy, zone)

    wgs_cx, wgs_cy = cgcs2000_to_wgs84(cx, cy, zone)
    # 将 WGS84 坐标转换为 GCJ02 坐标
    min_lon_gcj, min_lat_gcj = wgs84_to_gcj02(min_lon, min_lat)
    max_lon_gcj, max_lat_gcj = wgs84_to_gcj02(max_lon, max_lat)

    gcj_cx, gcj_cy = wgs84_to_gcj02(wgs_cx, wgs_cy)

    width = max(maxx - minx, 1)  # 计算地理范围的宽度
    height = max(maxy - miny, 1)  # 计算地理范围的高度

    # 根据地理范围的宽高比来确定图片的尺寸
    if width > height:
        size = f"1024*{int(1024 * (height / width))}"
    else:
        size = f"{int(1024 * (width / height))}*1024"
    print(f"图片大小：{size}")
    zoom = 11

    # 计算中心点
    center = f"{gcj_cx},{gcj_cy}"

    # 调用高德地图 API
    url = f"https://restapi.amap.com/v3/staticmap?key={GAODE_API_KEY}&location={center}&zoom={zoom}&size={size}&markers=mid,,A:{min_lon_gcj},{min_lat_gcj}"
    print(f"请求 URL: {url}")  # 打印请求 URL，方便调试
    try:
        # 发送请求
        response = requests.get(url)

        # 检查响应状态码
        if response.status_code == 200:
            # 尝试解析响应内容，判断是否为 JSON 格式（错误信息通常为 JSON）
            try:
                data = response.json()
                if 'status' in data and data['status'] == '0':
                    print(f"请求失败，错误信息: {data.get('info', '未知错误')}")
                    return None
            except ValueError:
                # 不是 JSON 格式，说明是正常的图片数据
                pass

            # 保存图片
            map_image_path = 'basemap.png'
            with open(map_image_path, 'wb') as f:
                f.write(response.content)
            print(f"地图截图已保存到 {map_image_path}")
            return map_image_path
        else:
            print(f"请求失败，状态码: {response.status_code}")
            return None
    except requests.RequestException as e:
        print(f"请求发生异常: {e}")
        return None


# CGCS2000投影坐标系（带分度带）转WGS84经纬度
def cgcs2000_to_wgs84(x, y, zone):
    """
    将 CGCS2000 投影坐标（带分度带）转换为 WGS84 经纬度坐标。
    :param x: CGCS2000 投影坐标 X
    :param y: CGCS2000 投影坐标 Y
    :param zone: 分度带代号（例如 35 表示 3度带第35带）
    :return: (lon, lat) WGS84 经纬度坐标
    """

    # 判断X坐标是否包含分度带带号
    if x >= 1000000:  # 如果X坐标是>6位数，则去掉分度带带号，因为后续计算是统一加上的
        # 如果X坐标是8位数，提取分度带带号
        zone_from_x = int(x // 1000000)
        if zone_from_x != zone:
            raise ValueError(f"X坐标的分度带带号 {zone_from_x} 与传入的分度带 {zone} 不一致")
        else:
            x = x - zone_from_x * 1000000

    # 定义 CGCS2000 投影坐标系（根据分度带）
    cgcs2000_crs = f"+proj=tmerc +lat_0=0 +lon_0={zone * 3} +k=1 +x_0=500000 +y_0=0 +ellps=GRS80 +units=m +no_defs"

    # 定义 WGS84 经纬度坐标系
    wgs84_crs = "EPSG:4326"

    # 创建坐标转换器
    transformer = Transformer.from_crs(cgcs2000_crs, wgs84_crs, always_xy=True)

    # 进行坐标转换
    lon, lat = transformer.transform(x, y)
    return lon, lat


# 生成工程图框
def generate_frame(doc, bbox):
    """
    生成工程图框
    :param doc: CAD工程dxf/dwg文件对象
    :param bbox: 地图范围
    """
    msp = doc.modelspace()
    # 图框参数（根据工程设计规范）
    frame_margin = 10  # 图框边距
    frame_width = bbox[2] - bbox[0] + 2 * frame_margin
    frame_height = bbox[3] - bbox[1] + 2 * frame_margin
    frame_x = bbox[0] - frame_margin
    frame_y = bbox[1] - frame_margin

    # 绘制图框
    msp.add_line((frame_x, frame_y), (frame_x + frame_width, frame_y))
    msp.add_line((frame_x + frame_width, frame_y), (frame_x + frame_width, frame_y + frame_height))
    msp.add_line((frame_x + frame_width, frame_y + frame_height), (frame_x, frame_y + frame_height))
    msp.add_line((frame_x, frame_y + frame_height), (frame_x, frame_y))


# 生成图例
def generate_legend(doc, layers, bbox):
    """
    生成图例信息。
    :param doc: DWG文档对象
    :param layers: 图层信息列表，格式为 [(layer_name, color, linewidth), ...]
    :param bbox: shp的总范围
    """
    msp = doc.modelspace()
    # 计算图形的宽度和高度
    width = bbox[2] - bbox[0]
    height = bbox[3] - bbox[1]

    # 根据图形大小动态调整间距和字体大小
    spacing_x = max(0.05 * width, 500)  # 水平间距为图形宽度的 5% 或至少 500
    spacing_y = max(0.02 * height, 20)  # 垂直间距为图形高度的 2% 或至少 20
    text_height = max(0.005 * height, 10)  # 文本高度为图形高度的 0.5% 或至少 10
    line_length = max(0.02 * width, 100)  # 示例线长度为图形宽度的 2% 或至少 100

    # 根据边界框计算图例起始位置（右上角）
    legend_x = bbox[2] - spacing_x
    legend_y = bbox[3] - spacing_y

    for i, (layer_name, color, linewidth) in enumerate(layers):
        # 添加图层名称文本
        msp.add_text(
            layer_name,
            dxfattribs={
                'height': text_height,  # 使用动态计算的文本高度
                'insert': (legend_x, legend_y - i * (text_height + spacing_y) - spacing_y * 0.2),  # 调整文本垂直间距
                'rotation': 0,
                'color': color,
                'style': '宋体'  # 使用宋体文字样式
            }
        )

        # 添加图层示例线
        start_x = legend_x - line_length
        end_x = legend_x - line_length * 0.5
        y_pos = legend_y - i * (text_height + spacing_y) - text_height / 2
        msp.add_line(
            (start_x, y_pos),
            (end_x, y_pos),
            dxfattribs={
                'color': color,
                'lineweight': linewidth
            }
        )


def wgs84_to_gcj02(lon, lat):
    """
    将WGS84坐标转换为GCJ02坐标（火星坐标系）。
    :param lon: WGS84经度
    :param lat: WGS84纬度
    :return: (gcj_lon, gcj_lat) GCJ02坐标
    """
    if out_of_china(lon, lat):
        return lon, lat

    a = 6378245.0  # 长半轴
    ee = 0.00669342162296594323  # 扁率

    dlat = transform_lat(lon - 105.0, lat - 35.0)
    dlon = transform_lon(lon - 105.0, lat - 35.0)

    rad_lat = lat / 180.0 * math.pi
    magic = math.sin(rad_lat)
    magic = 1 - ee * magic * magic
    sqrt_magic = math.sqrt(magic)

    dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrt_magic) * math.pi)
    dlon = (dlon * 180.0) / (a / sqrt_magic * math.cos(rad_lat) * math.pi)

    gcj_lat = lat + dlat
    gcj_lon = lon + dlon

    return gcj_lon, gcj_lat


def out_of_china(lon, lat):
    """
    判断坐标是否在中国以外。
    :param lon: 经度
    :param lat: 纬度
    :return: True表示坐标在中国以外，False表示在中国境内
    """
    return not (72.004 <= lon <= 137.8347 and 0.8293 <= lat <= 55.8271)


def transform_lat(lon, lat):
    ret = -100.0 + 2.0 * lon + 3.0 * lat + 0.2 * lat * lat + 0.1 * lon * lat + 0.2 * math.sqrt(abs(lon))
    ret += (20.0 * math.sin(6.0 * lon * math.pi) + 20.0 * math.sin(2.0 * lon * math.pi)) * 2.0 / 3.0
    ret += (20.0 * math.sin(lat * math.pi) + 40.0 * math.sin(lat / 3.0 * math.pi)) * 2.0 / 3.0
    ret += (160.0 * math.sin(lat / 12.0 * math.pi) + 320 * math.sin(lat * math.pi / 30.0)) * 2.0 / 3.0
    return ret


def transform_lon(lon, lat):
    ret = 300.0 + lon + 2.0 * lat + 0.1 * lon * lon + 0.1 * lon * lat + 0.1 * math.sqrt(abs(lon))
    ret += (20.0 * math.sin(6.0 * lon * math.pi) + 20.0 * math.sin(2.0 * lon * math.pi)) * 2.0 / 3.0
    ret += (20.0 * math.sin(lon * math.pi) + 40.0 * math.sin(lon / 3.0 * math.pi)) * 2.0 / 3.0
    ret += (150.0 * math.sin(lon / 12.0 * math.pi) + 300.0 * math.sin(lon / 30.0 * math.pi)) * 2.0 / 3.0
    return ret


if __name__ == "__main1__":
    bbox = [338708.852, 3283082.065, 377394.571, 3311514.623]
    minx, miny, maxx, maxy = bbox
    zone = 35
    # 将CGCS2000坐标转换为WGS84经纬度
    min_lon, min_lat = cgcs2000_to_wgs84(minx, miny, zone)
    max_lon, max_lat = cgcs2000_to_wgs84(maxx, maxy, zone)
    print(f"WGS84经度坐标1： {min_lon}, WGS84纬度坐标1：{min_lat}")
    print(f"WGS84经度坐标2： {max_lon}, WGS84纬度坐标2：{max_lat}")
    # 将 WGS84 坐标转换为 GCJ02 坐标
    min_lon_gcj, min_lat_gcj = wgs84_to_gcj02(min_lon, min_lat)
    max_lon_gcj, max_lat_gcj = wgs84_to_gcj02(max_lon, max_lat)
    print(f"GCJ02经度坐标1： {min_lon_gcj}, GCJ02纬度坐标1：{min_lat_gcj}")
    print(f"GCJ02经度坐标2： {max_lon_gcj}, GCJ02纬度坐标2：{max_lat_gcj}")

if __name__ == "__main__":
    x = 361889.096
    y = 3291640.635
    zone = 35
    # 将CGCS2000坐标转换为WGS84经纬度
    x1, y1 = cgcs2000_to_wgs84(x, y, zone)
    print(f"WGS84经度坐标1： {x1}, WGS84纬度坐标1：{y1}")

    gx, gy = wgs84_to_gcj02(x1, y1)
    print(f"GCJ02经度坐标1： {gx}, GCJ02纬度坐标1：{gy}")